/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Madason.MetaModel.Validation;

namespace Madason.MetaModel
{
	/// <summary>
	/// Type of State
	/// </summary>
	public enum StateType
	{
		Undefined,
		Initial,
		Simple,
		Composite,
		Final,
		Branch
	}


	/// <summary>
	/// State (used for StateMachines)
	/// </summary>
	public class State : ModelElement
	{
		public const string ERROR_STATE_HAS_NO_NAME = "The State has no valid name";
		public const string ERROR_CHOICEPOINT_NEED_ONE_CONDITION = "A choicepoint needs at least one outgooing transition with a condition";
		public const string ERROR_CHOICEPOINT_ONLY_ONE_ELSE = "A choicepoint may only one transition without ondition (else choice)";

		private StateType stateType;
		private StateList states = new StateList();
		private State parent;
		private StateTransitionList transitions = new StateTransitionList();
		private StateActionList entryActions = new StateActionList();
		private StateActionList exitActions = new StateActionList();

		public State Parent
		{
			get { return parent; }
			set { parent = value; }
		}

		private Class @class;

		public Class Class
		{
			get { return @class; }
			set { @class = value; }
		}


		/// <summary>
		/// Type of State
		/// </summary>
		public StateType StateType
		{
			get { return stateType; }
			set { stateType = value; }
		}
		
		/// <summary>
		/// encapsulated States
		/// </summary>
		public StateList States
		{
			get { return states; }
			set { states = value; }
		}

		
		/// <summary>
		/// Transitions
		/// </summary>
		public StateTransitionList Transitions
		{
			get { return transitions; }
			set { transitions = value; }
		}

		

		public StateActionList EntryActions
		{
			get { return entryActions; }
			set { entryActions = value; }
		}

		

		public StateActionList ExitActions
		{
			get { return exitActions; }
			set { exitActions = value; }
		}
	
	

		/// <summary>
		/// Search for a State by an id
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public State FindStateByID(string id)
		{
			if (Id == id)
				return this;

			foreach (State state in States)
			{
				State result = state.FindStateByID(id);
				if (result != null)
					return result;
			}

			return null;
		}


		public StateList SimpleStates
		{
			get
			{
				StateList list = new StateList();
				foreach (State st in States)
				{
					if (st.StateType == StateType.Simple)
						list.Add(st);
				}
				return list;
			}
		}

		public State InitialState
		{
			get
			{
				foreach (State st in States)
				{
					if (st.StateType == StateType.Initial)
					{
						if (st.Transitions.Count > 0)
							return st.Transitions[0].Target;
					}
				}
				return null;
			}
		}

		public StateTransitionTriggerList GetTriggerUnique()
		{
			Hashtable triggerMap = new Hashtable();
			StateTransitionTriggerList list = new StateTransitionTriggerList();
			foreach (State st in States)
			{
				foreach (StateTransition trans in st.Transitions)
				{
					foreach (StateTransitionTrigger trigger in trans.Trigger)
					{
						if (!triggerMap.Contains(trigger.Name))
						{
							triggerMap.Add(trigger.Name, trigger);
							list.Add(trigger);
						}
					}
				}
			}

			return list;
		}


		/// <summary>
		/// Get a transitions by a name of a trigger
		/// </summary>
		/// <param name="name">Name of the trigger</param>
		/// <returns>A List of transitions</returns>
		public StateTransitionList GetTransitionsByTriggerName(string name)
		{
			StateTransitionList list = new StateTransitionList();
			foreach (State st in States)
			{
				foreach (StateTransition trans in st.Transitions)
				{
					foreach (StateTransitionTrigger trigger in trans.Trigger)
					{
						if (trigger.Name == name)
						{
							list.Add(trans);
						}
					}
				}
			}

			return list;
		}


		/// <summary>
		/// Get Transitions with name (used by choicepoints for branches)
		/// </summary>
		/// <returns></returns>
		public StateTransitionList GetTransitionsWithGuard()
		{
			StateTransitionList list = new StateTransitionList();
			foreach (StateTransition trans in Transitions)
				if (trans.Guard != null && trans.Guard.Trim() != "")
					list.Add(trans);
			return list;			
		}


		/// <summary>
		/// Get Transitions with no name (used by choicepoints to get else branch)
		/// </summary>
		/// <returns></returns>
		public StateTransition GetTransitionWithoutGuard()
		{
			StateTransition transitionWithoutName = null;
			foreach (StateTransition trans in Transitions)
				if (trans.Guard == null || trans.Guard.Trim() == "")
					transitionWithoutName = trans;
			return transitionWithoutName;
		}

		public override void ValidateRecursive(ValidationIssueList issues)
		{
			base.ValidateRecursive(issues);
			foreach (State st in States)
				st.ValidateRecursive(issues);
			foreach (StateTransition trans in Transitions)
				trans.ValidateRecursive(issues);
		}

		/// <summary>
		/// Validate the state
		/// </summary>
		/// <param name="issues"></param>
		public override void Validate(Madason.MetaModel.Validation.ValidationIssueList issues)
		{
			if (String.IsNullOrEmpty(Name))
				issues.Add(new ValidationIssue(ERROR_STATE_HAS_NO_NAME, ValidationIssueSeverity.Error, this));

			if (stateType == StateType.Branch)
			{
				int withCond = 0;
				int withoutCond = 0;

				foreach (StateTransition trans in Transitions)
				{
					if (!String.IsNullOrEmpty(trans.Guard))
						withCond++;
					else
						withoutCond++;
				}

				if (withCond == 0) 
					issues.Add(new ValidationIssue(ERROR_CHOICEPOINT_NEED_ONE_CONDITION, ValidationIssueSeverity.Error, this));
				if (withoutCond > 1)
					issues.Add(new ValidationIssue(ERROR_CHOICEPOINT_ONLY_ONE_ELSE, ValidationIssueSeverity.Error, this));
			}
		}
	}


	/// <summary>
	/// List of states
	/// </summary>
	public class StateList : ArrayList
	{
		/// <summary>
		/// Access state by its index
		/// </summary>
		/// <param name="i"></param>
		/// <returns></returns>
		public new State this[int i]
		{
			get
			{
				return base[i] as State;
			}
			set
			{
				base[i] = value;
			}
		}

		/// <summary>
		/// Access state by its name
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public new State this[string name]
		{
			get
			{
				foreach (State st in this)
					if (st.Name == name)
						return st;
				return null;
			}

		}
	}
}
